﻿using System.Collections.Generic;
using UnityEngine;

public class MapTools {
	// === Public =====================================================================================================
	public static MapTools CreateInstance() {
		var instance = new MapTools();
		return instance;
	}

	public void OffsetPositionToZero(List<ElementCtrl> model) {
		var xMin = int.MaxValue;
		var yMin = int.MaxValue;
		foreach (var elementCtrl in model) {
			var pos = elementCtrl.ElementModel.Position;
			if (pos.x < xMin) { xMin = (int)pos.x; }
			if (pos.z < yMin) { yMin = (int)pos.z; }
		}
		foreach (var elementCtrl in model) {
			var oldPos = elementCtrl.ElementModel.Position;
			var newPos = new Vector3(oldPos.x - xMin, oldPos.y, oldPos.z - yMin);
			elementCtrl.SetPosition(newPos);
		}
	}

	public bool CheckBoundaryPerimeter(List<ElementCtrl> model) {
		var xMax = -1;
		var yMax = -1;
		foreach (var elementCtrl in model) {
			var pos = elementCtrl.ElementModel.Position;
			if (pos.x > xMax) { xMax = (int)pos.x; }
			if (pos.z > yMax) { yMax = (int)pos.z; }
		}
		var map = new ElementType[xMax + 1, yMax + 1];
		foreach (var elementCtrl in model) {
			var x = (int)elementCtrl.ElementModel.Position.x;
			var y = (int)elementCtrl.ElementModel.Position.z;
			map[x, y] = elementCtrl.ElementModel.ElementType;
		}
		return BeetleAlgorithm(map);
	}

	// === Private ====================================================================================================
	private bool BeetleAlgorithm(ElementType[,] map) {
		var direction = DirectionSnake.UP;
		var startPos = GetStartPos(map);
		if (startPos.Equals(-Vector2.one)) {
			return false;
		}

		var curPos = startPos;
		do {
			curPos = GetNextPos(curPos, direction);
			if (curPos.x < 0 || curPos.y < 0 || map.GetLength(0) <= curPos.x || map.GetLength(1) <= curPos.y) {
				direction = GetNextDir(direction, false);
			} else if (map[(int)curPos.x, (int)curPos.y].Equals(ElementType.None)) {
				direction = GetNextDir(direction, false);
			} else if (map[(int)curPos.x, (int)curPos.y].Equals(ElementType.Wall)) {
				direction = GetNextDir(direction, true);
			} else {
				return false;
			}
		} while (!startPos.Equals(curPos));
		return true;
	}

	private Vector2 GetStartPos(ElementType[,] map) {
		for (int i = 0; i < map.GetLength(0); i++) {
			if (map[i, 0].Equals(ElementType.None)) {
				continue;
			}
			if (map[i, 0].Equals(ElementType.Wall)) {
				return new Vector2(i, 0);
			}
			return -Vector2.one;
		}
		return -Vector2.one;
	}

	private Vector2 GetNextPos(Vector2 curPos, DirectionSnake direction) {
		switch (direction) {
			case DirectionSnake.UP:
				curPos.y++;
				break;
			case DirectionSnake.DOWN:
				curPos.y--;
				break;
			case DirectionSnake.LEFT:
				curPos.x--;
				break;
			case DirectionSnake.RIGHT:
				curPos.x++;
				break;
		}
		return curPos;
	}

	private DirectionSnake GetNextDir(DirectionSnake direction, bool isWall) {
		switch (direction) {
			case DirectionSnake.UP:
				direction = isWall ? DirectionSnake.LEFT : DirectionSnake.RIGHT;
				break;
			case DirectionSnake.DOWN:
				direction = isWall ? DirectionSnake.RIGHT : DirectionSnake.LEFT;
				break;
			case DirectionSnake.LEFT:
				direction = isWall ? DirectionSnake.DOWN : DirectionSnake.UP;
				break;
			case DirectionSnake.RIGHT:
				direction = isWall ? DirectionSnake.UP : DirectionSnake.DOWN;
				break;
		}
		return direction;
	}
}
